use ssh2::Session;
use std::fs;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::path::Path;

/**连接服务器 */
pub fn connect_server(host: String, username: String, password: String) -> Result<Session, String> {
    let host_with_port = if !host.contains(":") {
        format!("{}:22", host)
    } else {
        host.clone()
    };
    let tcp = TcpStream::connect(&host_with_port).map_err(|e| format!("连接服务器失败: {}", e))?;
    let mut sess: Session = Session::new().map_err(|e| format!("创建会话失败: {}", e))?;

    sess.set_tcp_stream(tcp);
    sess.set_timeout(15000); // 15秒超时
    sess.handshake().map_err(|e| format!("握手失败: {}", e))?;
    sess.userauth_password(&username, &password)
        .map_err(|e| format!("认证失败: {}", e))?;
    // 返回sess连接
    Ok(sess)
}

/**获取sftp回话 */
pub fn get_sftp(sess: &Session) -> Result<ssh2::Sftp, String> {
    let sftp = sess
        .sftp()
        .map_err(|e| format!("创建SFTP会话失败: {}", e))?;
    // 返回SFTP会话
    Ok(sftp)
}

/**执行服务器命令 */
pub fn run_command(sess: &Session, command: String) -> Result<String, String> {
    let mut channel = sess
        .channel_session()
        .map_err(|e| format!("channel初始化失败: {}", e))?;
    channel
        .exec(&command)
        .map_err(|e| format!("命令执行错误: {}", e))?;
    let mut data = String::new();
    channel
        .read_to_string(&mut data)
        .map_err(|e| format!("读取结果错误: {}", e))?;
    channel
        .wait_close()
        .map_err(|e| format!("关闭错误: {}", e))?;
    Ok(data)
}

/**上传文件 */
pub fn upload_file<F>(
    sftp: &ssh2::Sftp,
    local_path: &String,
    remote_path: &String,
    mut progress_callback: F,
) -> Result<(), String>
where
    F: FnMut(f64, usize, f64),
{
    let remote_file_path = Path::new(&remote_path);
    let local_file_path = Path::new(&local_path);

    // 读取本地文件
    let version_content =
        fs::read(&local_file_path).map_err(|e| format!("读取版本文件失败: {}", e))?;

    // 上传文件
    let mut remote_file = sftp
        .create(&remote_file_path)
        .map_err(|e| format!("上传远程文件失败: {}", e))?;

    let total_size = version_content.len() as f64;
    let mut bytes_written = 0;

    // 循环写入文件防止数据丢失
    while bytes_written < version_content.len() {
        let n = remote_file
            .write(&version_content[bytes_written..])
            .map_err(|e| format!("写入远程文件失败: {}", e))?;
        bytes_written += n;
        let progress = (bytes_written as f64 / total_size) * 100.0;
        progress_callback(progress, bytes_written, total_size);
    }

    Ok(())
}

// 从远程服务器下载文件到本地备份
pub fn download_for_backup(
    sftp: &ssh2::Sftp,
    remote_path: &Path,
    backup_path: &Path,
) -> Result<(), String> {
    // 确保备份目录存在
    fs::create_dir_all(backup_path).map_err(|e| format!("创建备份目录失败: {}", e))?;

    // 读取远程目录内容
    let remote_dir = match sftp.readdir(remote_path) {
        Ok(dir) => dir,
        Err(_) => {
            // 如果目录不存在，直接返回
            return Ok(());
        }
    };

    // 遍历并下载每个文件
    for (path, stat) in remote_dir {
        let file_name = path
            .file_name()
            .ok_or_else(|| "无效的文件名".to_string())?
            .to_str()
            .ok_or_else(|| "文件名编码错误".to_string())?;

        let remote_file_path = remote_path.join(file_name);
        let backup_file_path = backup_path.join(file_name);

        if stat.is_dir() {
            // 如果是目录递归下载
            download_for_backup(sftp, &remote_file_path, &backup_file_path)?;
        } else {
            // 如果是文件，直接下载
            let mut remote_file = sftp
                .open(&remote_file_path)
                .map_err(|e| format!("打开远程文件失败 {}: {}", file_name, e))?;

            let mut contents = Vec::new();
            remote_file
                .read_to_end(&mut contents)
                .map_err(|e| format!("读取远程文件失败 {}: {}", file_name, e))?;

            fs::write(&backup_file_path, contents)
                .map_err(|e| format!("写入备份文件失败 {}: {}", file_name, e))?;
        }
    }

    Ok(())
}
